#include "ODN_BaseGame.h"

#include <TinyXML\TinyXML.h>

#include "ODN_OgreInputDevice.h"


class CFactory
{
	
public:
	
	create()
	
private:
	
	
	
};

CGameWorld::addEntityFactory(CFactory* pEntityFactory)
{
	
	
	
}


namespace ouden {

CBaseGame::CBaseGame()
{
	//ctor
	
	//We're only just getting started
	m_bFinished = false;
	
	//NULL the uninitialised pointers for safety
	m_pRoot = 0;
	m_pEventProcessor = 0;
	m_pInputDevice = 0;
	//m_pGameStateMgr = 0;
	m_pGameWorld = 0;
	
}

CBaseGame::~CBaseGame()
{
	//dtor
	
	//Exit all the states
	while (!m_pGameStates.empty()) {
		m_pGameStates.back()->exit();
		m_pGameStates.pop_back();
	}
	
	//Delete the dynamically allocated memory if required
	//if (m_pGameStateMgr)
	//	delete m_pGameStateMgr;
	if (m_pInputDevice)
		delete m_pInputDevice;
	if (m_pEventProcessor)
		delete m_pEventProcessor;
	if (m_pRoot)
		delete m_pRoot;
	if (m_pGameWorld)
		delete m_pGameWorld;
	
}

bool CBaseGame::init()
{
	
	//Create the root Ogre object
	m_pRoot = new Ogre::Root();
	
	// Show the configuration dialog and initialise the system
	// You can skip this and use root.restoreConfig() to load configuration
	// settings if you were sure there are valid ones saved in ogre.cfg
	if(m_pRoot->showConfigDialog())
	{
		// If returned true, user clicked OK so initialise
		// Here we choose to let the system create a default rendering window by passing 'true'
		m_pWindow = m_pRoot->initialise(true, "Ouden Engine Game");
	}
	else
	{
		return false;
	}
	
	//Load all the game resources
	loadResources();
	
	//Create the Ogre scene manager
	m_pSceneMgr = m_pRoot->getSceneManager(Ogre::ST_GENERIC);
	
	///////////////////////
	//Setup the main camera
	///////////////////////
	// Create the camera
	m_pCamera = m_pSceneMgr->createCamera("MainCam");
	// Position it at the origin.
	m_pCamera->setPosition(Ogre::Vector3(0, 0, 0));
	// Look back along -Z
	m_pCamera->lookAt(Ogre::Vector3(0,0,-300));
	m_pCamera->setNearClipDistance(0.5);
	
	// Create one viewport, entire window
	Ogre::Viewport* vp = m_pWindow->addViewport(m_pCamera);
	vp->setBackgroundColour(Ogre::ColourValue(1,1,1));
	// Alter the camera aspect ratio to match the viewport
	m_pCamera->setAspectRatio(Ogre::Real(vp->getActualWidth()) / Ogre::Real(vp->getActualHeight()));
	
	// Initialise, parse scripts etc
	Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
	
	//TODO: Loading screen as mentioned in ExampleApplication setup resource listener
	
	//Create the opal simulator
	m_pSimulator = opal::createSimulator();
	m_pSimulator->setGravity(opal::Vec3r(0, (opal::real)-9.81, 0));
	
	//Let Ogre know that this is a frame listener
	m_pRoot->addFrameListener(this);
	
	//////////////////////
	//Setup input handling
	//////////////////////
	//Create and initialise a new event processor
	m_pEventProcessor = new Ogre::EventProcessor();
	m_pEventProcessor->initialise(m_pWindow);
	m_pEventProcessor->startProcessingEvents();
	//Register this class as a keylistener
	m_pEventProcessor->addKeyListener(this);
	//Get a pointer to an input reader device
	//m_pInputReader = m_pEventProcessor->getInputReader();
	
	//Create an input device
	//TODO: Check where this input device should be decided
	m_pInputDevice = new COgreInputDevice(m_pEventProcessor);
	
	//Create a game state manager object
	//m_pGameStateMgr = new CGameStateMgr();
	//m_pGameStateMgr->changeState(CNullGameState::getInstance())
	
	//Create an instance of a game world
	m_pGameWorld = new CGameWorld();
	
	
	//Call the function to create the scene
	//createScene();
	std::cout << "End of init" << std::endl;
	return true;
	
}

bool CBaseGame::loadResources()
{
	
	// Load resource paths from config file
	Ogre::ConfigFile cf;
	cf.load("resources.cfg");
	
	// Go through all sections & settings in the file
	Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();

	String secName, typeName, archName;
	while (seci.hasMoreElements())
	{
		secName = seci.peekNextKey();
		Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
		Ogre::ConfigFile::SettingsMultiMap::iterator i;
		for (i = settings->begin(); i != settings->end(); ++i)
		{
			typeName = i->first;
			archName = i->second;
			Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
				archName, typeName, secName);
		}
	}
	
	return true;
	
}

void CBaseGame::start()
{
	
	//Initialise the engine
	if (!init())
		return;
	
	//Begin the game
	m_pRoot->startRendering();
	
	//Call the function to destroy the scene
	//destroyScene();
	
}

bool CBaseGame::frameStarted(const Ogre::FrameEvent &evt)
{
	
	//Let the current game state process unbuffered input
	m_pGameStates.back()->processUnBufInput(m_pInputDevice);
	
	return m_pGameStates.back()->frameStarted( );
	/*
	std::cout << "Frame Start" << std::endl;
	//Check for escape key exit
	if(m_pInputReader->isKeyDown(Ogre::KC_ESCAPE))
		return false;
	
	//Capture input
	m_pInputReader->capture();
	
	//Pass input to the game world
	m_pGameWorld->processInput(m_pInputReader);
	
	//Update the gameworld
	m_pGameWorld->update();
	
	return true;
	*/
}

bool CBaseGame::frameEnded(const Ogre::FrameEvent &evt)
{
	
	return m_pGameStates.back()->frameEnded( );
	
	/*
	if (m_bFinished)
		return false;
	
	return true;
	*/
}

//Buffered key events get passed to the current game state class
void CBaseGame::keyPressed(Ogre::KeyEvent *e)
{
	
	m_pGameStates.back()->keyDown(e->getKey());
	
}
void CBaseGame::keyReleased(Ogre::KeyEvent *e)
{
	
	m_pGameStates.back()->keyUp(e->getKey());
	
}
void CBaseGame::keyClicked(Ogre::KeyEvent *e)
{
	
	m_pGameStates.back()->keyClicked(e->getKey());
	
}

void CBaseGame::changeState(CGameState* state)
{
	// cleanup the current state
	if ( !m_pGameStates.empty() ) {
		m_pGameStates.back()->exit();
		m_pGameStates.pop_back();
	}

	// store and init the new state
	m_pGameStates.push_back(state);
	m_pGameStates.back()->enter();
}

void CBaseGame::pushState(CGameState* state)
{
	// pause current state
	if ( !m_pGameStates.empty() ) {
		m_pGameStates.back()->pause();
	}

	// store and init the new state
	m_pGameStates.push_back(state);
	m_pGameStates.back()->enter();
}

void CBaseGame::popState()
{
	// cleanup the current state
	if ( !m_pGameStates.empty() ) {
		m_pGameStates.back()->exit();
		m_pGameStates.pop_back();
	}

	// resume previous state
	if ( !m_pGameStates.empty() ) {
		m_pGameStates.back()->resume();
	}
}

void CBaseGame::createModelEntity(String name, String modelFile, Vector pos, Angle orientation)
{
	std::cout << "1 " << m_pSceneMgr << std::endl;
	//Load the mesh into an Ogre entity
	Ogre::Entity* ogreEntity = m_pSceneMgr->createEntity(name, modelFile + ".mesh");
	ogreEntity->setMaterialName("Plastic/Red");
	// Keep the normals normalized even after scaling.
	ogreEntity->setNormaliseNormals(true);
	std::cout << "2" << std::endl;
	//Load physics blueprint from file
	opal::Blueprint blueprint;
	opal::loadFile(blueprint, MODEL_PATH + modelFile + " blueprint.xml");
	opal::Matrix44r transform;
	transform.translate(pos[0], pos[1], pos[2]);
	//Instantiate the Blueprint.
	opal::BlueprintInstance blueprintInstance;
	m_pSimulator->instantiateBlueprint(blueprintInstance, blueprint, transform, 16);
	std::cout << "3" << std::endl;
	//Create the new model entity
	//TODO: Constructor parameters
	CModelEntity* newModelEntity = new CModelEntity(name);
	std::cout << "4" << std::endl;
	//Now we must load our own special model file in order to link the solids from the blueprint
	// to the bones in the skeleton
	
	//Open our model file
	TiXmlDocument doc( (MODEL_PATH + modelFile + " model.xml").c_str() );
	doc.LoadFile();
	//TODO: Error checking
	//Create a document handle
	TiXmlHandle docHandle( &doc );
	//Get the model data
	TiXmlElement* nodeModel = docHandle.FirstChild( "Model" ).Element();
	
	std::cout << "solids " << blueprintInstance.getNumSolids() << std::endl;
	
	//Loop through each solid
	for (int i=0; i<blueprintInstance.getNumSolids(); ++i) {
		
		//Get a pointer to the solid
		opal::Solid* opalSolid = blueprintInstance.getSolid(i);
		
		//Check if the mesh has a skeleton
		//TODO: WARNINGNESS This should maybe be outside the loop so the loop is inside the if
		// Can you have more than one solid but no skeleton? 
		if (ogreEntity->hasSkeleton()) {
			
			//If so then we need to attach each solid to its proper bone according to our model file data
			
			//Get the first solid to bone assignment
			TiXmlElement* nodeAssignment = nodeModel->FirstChildElement( "SolidToBoneAssignment" );
			
			//Read the index of the solid
			int solidIndex;
			nodeAssignment->QueryIntAttribute( "solidindex" , &solidIndex);
			
			//Read the name of the bone
			String boneName = nodeAssignment->Attribute( "bonename" );
			
			//Get pointer to the bone from the mesh skeleton
			Ogre::Node* boneNode = ogreEntity->getSkeleton()->getBone(boneName);
			
			//Create a new model bit, linking the solid to the bone
			CModelBit* newModelBit = new CModelBit(opalSolid, boneNode);
			
			//Add the new bit to the model
			newModelEntity->addBit(newModelBit);
			
		}
		else
		{
			
			//If no skeleton then just make a single scene node and attach the mesh to it
			
			//Create an Ogre SceneNode for the skeletonless model entity
			Ogre::SceneNode* ogreSceneNode = m_pSceneMgr->getRootSceneNode()->createChildSceneNode(name);
			
			//Attach the entity to the scene node
			ogreSceneNode->attachObject(ogreEntity);
			
			//Create a new model bit, linking the solid to the scene node
			CModelBit* newModelBit = new CModelBit(opalSolid, ogreSceneNode);
			
			//Add the new bit to the model
			newModelEntity->addBit(newModelBit);
			
		}
		
	}
	
	
}

} //namespace ouden
